
/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    uda1341.c
 * @brief   Asoc框架下的codec部分，uda1341编解码芯片的iis与控制接口相关操作
 *          参考sound\soc\codecs\uda134x.c
 *
 * @note    1、构建一个snd_soc_dai_driver
 *          2、构建一个snd_soc_codec_driver
 *          3、注册它们
 * @author  K
 * @version 0.0.1
 * @date    2021-08-07
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-08-07   | v0.0.1    | Kcode   | uda1341编解码芯片的iis与控制接口相关操作
 * -----------------------------------------------------------------------------
 ******************************************************************************/
 
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <asm/io.h>

#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)

/* UDA1341寄存器的编号 */
#define UDA1341_DATA00   0
#define UDA1341_DATA01   1
#define UDA1341_DATA10   2
#define UDA1341_EA000     3
#define UDA1341_EA001     4
#define UDA1341_EA010     5
#define UDA1341_EA100     6
#define UDA1341_EA101     7
#define UDA1341_EA110     8
#define UDA1341_DATA1     9
#define UDA1341_STATUS0 10
#define UDA1341_STATUS1 11

#define UDA1341_REGS_NUM 12  /**< UDA1341寄存器的数量 */

/* UDA1341寄存器的地址 */
#define UDA1341_L3ADDR	   5
#define UDA1341_DATA0_ADDR	  ((UDA1341_L3ADDR << 2) | 0)
#define UDA1341_DATA1_ADDR	  ((UDA1341_L3ADDR << 2) | 1)
#define UDA1341_STATUS_ADDR	((UDA1341_L3ADDR << 2) | 2)

/* UDA1341寄存器所需要的置位 */
#define UDA1341_EXTADDR_PREFIX	0xC0
#define UDA1341_EXTDATA_PREFIX	0xE0

/* UDA1341与控制芯片的IO寄存器 */
static volatile unsigned int *gpbdat;
static volatile unsigned int *gpbcon;

/* UDA1341状态控制 */
#define STAT0              (0x00)
#define STAT0_RST          (1 << 6)
#define STAT0_SC_MASK      (3 << 4)
#define STAT0_SC_512FS     (0 << 4)
#define STAT0_SC_384FS     (1 << 4)
#define STAT0_SC_256FS     (2 << 4)
#define STAT0_IF_MASK      (7 << 1)
#define STAT0_IF_I2S       (0 << 1)
#define STAT0_IF_LSB16     (1 << 1)
#define STAT0_IF_LSB18     (2 << 1)
#define STAT0_IF_LSB20     (3 << 1)
#define STAT0_IF_MSB       (4 << 1)
#define STAT0_IF_LSB16MSB  (5 << 1)
#define STAT0_IF_LSB18MSB  (6 << 1)
#define STAT0_IF_LSB20MSB  (7 << 1)
#define STAT0_DC_FILTER    (1 << 0)
#define STAT0_DC_NO_FILTER (0 << 0)

#define STAT1 (0x80)
#define STAT1_DAC_GAIN (1 << 6) /* gain of DAC */
#define STAT1_ADC_GAIN (1 << 5) /* gain of ADC */
#define STAT1_ADC_POL (1 << 4) /* polarity of ADC */
#define STAT1_DAC_POL (1 << 3) /* polarity of DAC */
#define STAT1_DBL_SPD (1 << 2) /* double speed playback */
#define STAT1_ADC_ON (1 << 1) /* ADC powered */
#define STAT1_DAC_ON (1 << 0) /* DAC powered */

/* data0 direct control */
#define DATA0 (0x00)
#define DATA0_VOLUME_MASK (0x3f)
#define DATA0_VOLUME(x) (x)

#define DATA1 (0x40)
#define DATA1_BASS(x) ((x) << 2)
#define DATA1_BASS_MASK (15 << 2)
#define DATA1_TREBLE(x) ((x))
#define DATA1_TREBLE_MASK (3)

#define DATA2 (0x80)
#define DATA2_PEAKAFTER (0x1 << 5)
#define DATA2_DEEMP_NONE (0x0 << 3)
#define DATA2_DEEMP_32KHz (0x1 << 3)
#define DATA2_DEEMP_44KHz (0x2 << 3)
#define DATA2_DEEMP_48KHz (0x3 << 3)
#define DATA2_MUTE (0x1 << 2)
#define DATA2_FILTER_FLAT (0x0 << 0)
#define DATA2_FILTER_MIN (0x1 << 0)
#define DATA2_FILTER_MAX (0x3 << 0)
/* data0 extend control */
#define EXTADDR(n) (0xc0 | (n))
#define EXTDATA(d) (0xe0 | (d))

#define EXT0 0
#define EXT0_CH1_GAIN(x) (x)
#define EXT1 1
#define EXT1_CH2_GAIN(x) (x)
#define EXT2 2
#define EXT2_MIC_GAIN_MASK (7 << 2)
#define EXT2_MIC_GAIN(x) ((x) << 2)
#define EXT2_MIXMODE_DOUBLEDIFF (0)
#define EXT2_MIXMODE_CH1 (1)
#define EXT2_MIXMODE_CH2 (2)
#define EXT2_MIXMODE_MIX (3)
#define EXT4 4
#define EXT4_AGC_ENABLE (1 << 4)
#define EXT4_INPUT_GAIN_MASK (3)
#define EXT4_INPUT_GAIN(x) ((x) & 3)
#define EXT5 5
#define EXT5_INPUT_GAIN(x) ((x) >> 2)
#define EXT6 6
#define EXT6_AGC_CONSTANT_MASK (7 << 2)
#define EXT6_AGC_CONSTANT(x) ((x) << 2)
#define EXT6_AGC_LEVEL_MASK (3)
#define EXT6_AGC_LEVEL(x) (x)

/*!
 * @brief  uda1413与控制芯片的控制寄存器的初始化
 * @return vois
 */
static void uda1341_gpiocon_init(void)
{
    *gpbcon &= ~((3 << 4) | (3 << 6) | (3 << 8));
    *gpbcon |= ((1 << 4) | (1 << 6) | (1 << 8));
}

static int uda1341_write_reg(struct snd_soc_codec *codec, unsigned int reg,
    unsigned int value);

/*!
 * @brief  通过控制接口(GPB2,3,4)读写寄存器进行初始化
 * @return void
 */
 void uda1341_init_regs(struct snd_soc_codec *codec)
 {
    uda1341_gpiocon_init();

    /* 复位 */
    uda1341_write_reg(codec, UDA1341_STATUS0, 0x40 | STAT0_SC_384FS | STAT0_DC_FILTER);

    /* 启动ADC、DAC */
    uda1341_write_reg(codec, UDA1341_STATUS1, STAT1_ADC_ON | STAT1_DAC_ON);

    /* 设置最大音量 */
    uda1341_write_reg(codec, UDA1341_DATA00, DATA0_VOLUME(0x0));
    
    uda1341_write_reg(codec, UDA1341_DATA01, DATA1_BASS(0)| DATA1_TREBLE(0));

    /* 设置为非静模式 */
    uda1341_write_reg(codec, UDA1341_DATA10, 0);
 }

 static int uda1341_soc_probe(struct snd_soc_codec *codec)
 { 
     uda1341_init_regs(codec);
     return 0;
 }

/*!
 * 定义uda1341芯片的所有寄存器的默认值
 */
static const char s_uda1341_reg[UDA1341_REGS_NUM] = {
    /* DATA0 */
    0x00, 0x40, 0x80,

    /* 扩展寄存器 */
    0x04, 0x04, 0x04, 0x00, 0x00, 0x00,

    /* DATA1 */
    0x00,

    /* 状态寄存器 */
    0x00, 0x83,
};

/*!
 * 定义uda1341芯片的所有寄存器的地址
 */
static const char s_uda1341_reg_addr[UDA1341_REGS_NUM] = {
    /* DATA0 */
    UDA1341_DATA0_ADDR, UDA1341_DATA0_ADDR, UDA1341_DATA0_ADDR,

    /* 扩展寄存器 */
    0, 1, 2, 4, 5, 6,

    /* DATA1 */
    UDA1341_DATA1_ADDR,

    /* 状态寄存器 */
    UDA1341_STATUS_ADDR, UDA1341_STATUS_ADDR, 
};

/*!
 * 定义uda1341芯片的寄存器所需的置位
 */
static const char s_uda1341_data_bit[UDA1341_REGS_NUM] = {
    /* DATA0 */
    0, (1 << 6), (1 << 7),

    /* 扩展寄存器访问方式不同，全部设置为0，忽略 */
    0, 0, 0, 0, 0,

    /* DATA1，设置为0，忽略 */
    0, 

    /* 状态寄存器 */
    0, (1 << 7),
};

/*!
 * @brief  读取uda1341芯片的指定寄存器的值
 * @return int 失败：-1，成功：寄存器的地址
 */
static inline unsigned int uda1341_read_reg_cache(struct snd_soc_codec *codec,
	unsigned int reg)
{
	u8 *cache = codec->reg_cache;

	if (reg >= UDA1341_REGS_NUM)
		return -1;
	return cache[reg];
}

/*!
 * @brief  设置wm8976芯片的MOD引脚的电平
 * @param  [in] val  0-低电平，1-高电平
 * @return void
 */
 static void set_mod(int val)
 {
     if (val)
     {
         *gpbdat |= (1<<2);
     }
     else
     {
         *gpbdat &= ~(1<<2);
     }
 }

/*!
 * @brief  设置wm8976芯片的CLK引脚的电平
 * @param  [in] val  0-低电平，1-高电平
 * @return void
 */
 static void set_clk(int val)
 {
     if (val)
     {
         *gpbdat |= (1<<4);
     }
     else
     {
         *gpbdat &= ~(1<<4);
     }
 }

/*!
 * @brief  设置wm8976芯片的DAT引脚的电平
 * @param  [in] val  0-低电平，1-高电平
 * @return void
 */
 static void set_dat(int val)
 {
     if (val)
     {
         *gpbdat |= (1<<3);
     }
     else
     {
         *gpbdat &= ~(1<<3);
     }
 }

 /*!
  * @brief  往uda1341芯片的指定寄存器写入一字节的数据
  * @return void
  */
 static void sendbyte(unsigned int byte)
 {
     int i;
 
     for (i = 0; i < 8; i++) {
         set_clk(0);
         udelay(1);
         set_dat(byte & 1);
         udelay(1);
         
         set_clk(1);
         udelay(1);
         byte >>= 1;
     }
 }

/*!
 * @brief  写uda1341芯片的寄存器时的时序操作
 * @return void
 */
static void l3_write(u8 addr, u8 data)
{
    set_clk(1);
    set_dat(1);
    set_mod(1);
    udelay(1);

    /* 发出地址信号 */
    set_mod(0);
    udelay(1);
    sendbyte(addr);
    udelay(1);

    /* 发出数据信号 */
    set_mod(1);
    udelay(1);
    sendbyte(data);

    set_clk(1);
    set_dat(1);
    set_mod(1);
}

/*!
 * @brief  往uda1341芯片的指定寄存器写入值
 * @return int 0：成功，-1：失败
 */
static int uda1341_write_reg(struct snd_soc_codec *codec, unsigned int reg,
    unsigned int value)
{
    u8 *cache = codec->reg_cache;

    /*!
     * 先保存
     */
    if (reg >= UDA1341_REGS_NUM)
    	return -1;
    cache[reg] = value;

    /*!
     * 后写入硬件
     */
    /* 对于EA，需要调用2次l3_write */
    if ((reg >= UDA1341_EA000) && (reg <= UDA1341_EA110)) {
        l3_write(UDA1341_DATA0_ADDR, s_uda1341_reg_addr[reg] | UDA1341_EXTADDR_PREFIX);
        l3_write(UDA1341_DATA0_ADDR, value | UDA1341_EXTDATA_PREFIX);
    } else {
        l3_write(s_uda1341_reg_addr[reg], value | s_uda1341_data_bit[reg]);
    }
    
    return 0;
}

/*!
 * 核心结构体snd_soc_codec_driver(编解码芯片的控制相关)
 */
static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {
    .probe = uda1341_soc_probe,

    /*!
     * uda1341的寄存器不支持当前的读操作
     * 因此：要知道某个寄存器的当前值，只能在写入时保存值
     * 所以：需要开辟一个内存大小为其寄存器大小的buffer
     */
    .reg_cache_size    = sizeof(s_uda1341_reg),
    .reg_word_size     = sizeof(u8),
    .reg_cache_default = s_uda1341_reg,
    .reg_cache_step    = 1,
    .read              = uda1341_read_reg_cache,
    .write             = uda1341_write_reg,
};

/*!
 * @brief  uda1341芯片的寄存器设置
 * @return int
 */
static int uda1341_hw_params(struct snd_pcm_substream *substream,
                    struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
    /*！
     * 根据params的值，设置uda1341的寄存器
     * 如：时钟的设置、格式
     */

	return 0;
}

/*!
 * 编解码芯片的具体操作函数
 */
static const struct snd_soc_dai_ops uda1341_dai_ops = {
	.hw_params	= uda1341_hw_params,

};

/*!
 * 核心结构体snd_soc_dai_driver(编解码芯片的相关属性)
 */
static struct snd_soc_dai_driver uda1341_dai = {
	.name = "uda1341-iis",
	/* 播放功能 */
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* 录音功能 */
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* pcm operations */
	.ops = &uda1341_dai_ops,
};

/*!
 * @brief 关闭设备的函数，必须得有，否则程序编译报错
 * @return void
 */
static void uda1341_dev_release(struct device *dev)
{}

/*!
 * 分配注册一个名为uda1341_dev的平台设备
 */
static struct platform_device uda1341_dev = {
    .name = "uda1341-codec",
    .id   = -1,
    .dev  = {
        .release = uda1341_dev_release,
    },
};

/*!
 * @brief  根据名字，连接到对应的平台驱动与设备时所调用的函数
 * @return int
 */
static int uda1341_probe(struct platform_device * pdev)
{    
    return snd_soc_register_codec(&pdev->dev, 
        &soc_codec_dev_uda1341, &uda1341_dai, 1);
}

/*!
 * @brief 关闭平台驱动的函数，必须得有，否则程序编译报错
 * @return int
 */
static int uda1341_remove(struct platform_device * pdev)
{
    snd_soc_unregister_codec(&pdev->dev);
    return 0;
}

/*!
 * 分配注册一个名为uda1341_dev的平台驱动
 */
struct platform_driver uda1341_drv = {
    .probe   = uda1341_probe,
    .remove  = uda1341_remove,
    .driver  = {
        .name = "uda1341-codec",
    },
};

/*!
 * @brief  uda1413与控制芯片的IO寄存器的映射
 * @return vois
 */
static void uda1341_gpio_map(void)
{
    gpbcon = ioremap(0x56000010, 4);
    gpbdat = ioremap(0x56000014, 4);
}

/*!
 * @brief  uda1413与控制芯片的IO寄存器的取消映射
 * @return vois
 */
static void uda1341_gpio_unmap(void)
{
    iounmap(gpbdat);
    iounmap(gpbcon);
}

/*!
 * @brief 入口函数
 * @return int 0：注册成功，1：注册失败
 */
static int uda1341_init(void)
{
    int ret = 0;

    uda1341_gpio_map();
    
    ret = platform_device_register(&uda1341_dev);
    if (ret) {
        printk("Can not register platform uda1341_dev!\n");
        goto fail;
    }
    
    ret = platform_driver_register(&uda1341_drv);
    if (ret) {
        printk("Can not register platform uda1341_drv!\n");
        platform_device_unregister(&uda1341_dev);
        goto fail;
    }

fail:    
    return ret;
}

/*!
 * @brief 出口函数
 * @return int，
 */
static void uda1341_exit(void)
{
    uda1341_gpio_unmap();

    platform_driver_unregister(&uda1341_drv);
    platform_device_unregister(&uda1341_dev);
}

/* 修饰 */
module_init(uda1341_init);
module_exit(uda1341_exit);

/* 协议 */
MODULE_LICENSE("GPL");


